package com.prodog.command.core;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.prodog.command.annonations.Default;
import com.prodog.command.entity.CommandParam;
import com.prodog.command.entity.CommandResult;
import com.prodog.command.entity.GameCommand;
import com.prodog.command.entity.RoleGameCommand;
import com.prodog.usermodule.role.entity.RoleInfo;
import com.prodog.usermodule.role.wrapper.RoleInfoWrapper;
import com.prodog.usermodule.user.entity.UserInfo;
import com.prodog.usermodule.user.wrapper.UserInfoWrapper;
import com.prodog.utils.bean.BeanUtil;
import com.prodog.utils.bean.ProxyUtil;
import com.prodog.utils.dataType.ConvertUtil;
import com.prodog.utils.string.RegexUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class GameCommandHandler {
    private final GameCommandCentral central;
    private final UserInfoWrapper userInfoWrapper;
    private final RoleInfoWrapper roleInfoWrapper;

    /***
     * 处理命令
     * @param command
     * @return
     */
    public CommandResult handle(GameCommand command) {
        String regex = central.getRegexByCommandStr(command.getCommand());
        if (regex != null) {
            Method commandMethod = central.getMethodByRegex(regex);
            Object commandBean = central.getCommandBeanByRegex(regex);

            Map<String, Object> methodArgs = buildMethodArgsMap(command, commandBean, commandMethod, regex);

            Object[] args = methodArgs.values().toArray(new Object[0]);
            try {
                CommandResult res = (CommandResult) commandMethod.invoke(commandBean, args);
                return res;
            } catch (Exception e) {
                e.printStackTrace();
                return CommandResult.failure("服务器发生错误。");
            }
        }
        return null;
    }

    /***
     * 获取命令方法的参数(方法调用参数和拦截器参数)
     * @param command
     * @param commandMethod
     * @param regex
     * @return
     */
    private Map<String, Object> buildMethodArgsMap(GameCommand command, Object commandBean, Method commandMethod, String regex) {
        Parameter[] parameters = commandMethod.getParameters();
        Class[] parameterTypes = commandMethod.getParameterTypes();
        Annotation[][] annoss = commandMethod.getParameterAnnotations();
        List<String> props = central.getPropsByRegex(regex);

        //兼容AOP
        if (AopUtils.isAopProxy(commandBean)) {
            try {
                Object superBean = ProxyUtil.getTarget(commandBean);
                Method superMethod = ReflectUtil.getMethod(superBean.getClass(), commandMethod.getName(), parameterTypes);
                parameters = superMethod.getParameters();
                annoss = superMethod.getParameterAnnotations();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        List<CommandParam> gParams = getCommandParams(regex, command.getCommand(), props);
        Map<String, Object> invokeArgsMap = new LinkedHashMap<>();
        for (int i = 0; i < parameterTypes.length; i++) {
            Class type = parameterTypes[i];
            String name = parameters[i].getName();
            Annotation[] annos = annoss[i];
            if (type.equals(GameCommand.class)) {
                invokeArgsMap.put(name, command);
                continue;
            } else if (type.equals(RoleGameCommand.class)) {
                RoleGameCommand roleGameCommand = BeanUtil.beanToBean(command, RoleGameCommand.class);
                roleGameCommand.setUserId(command.getFromQQ());
                UserInfo userInfo = userInfoWrapper.getById(command.getFromQQ());
                if (userInfo != null) {
                    RoleInfo roleInfo = roleInfoWrapper.getById(userInfo.getRoleId());
                    roleGameCommand.setRoleId(roleInfo.getId());
                    roleGameCommand.setUserInfo(userInfo);
                    roleGameCommand.setRoleInfo(roleInfo);
                }
                invokeArgsMap.put(name, roleGameCommand);
            } else if (ConvertUtil.isSimpleType(type)) {
                boolean hasProp = false;
                for (CommandParam param : gParams) {
                    if (param.getProp().equals(name) && !StrUtil.isBlank(param.getValue())) {
                        invokeArgsMap.put(name, ConvertUtil.convert(param.getValue(), type));
                        hasProp = true;
                    }
                }
                if (!hasProp) {
                    invokeArgsMap.put(name, null);
                    for (Annotation anno : annos) {
                        if (anno instanceof Default) {
                            invokeArgsMap.put(name, ConvertUtil.convert(((Default) anno).value(), type));
                        }
                    }
                }
                //对象类型注入
            } else {
                try {
                    Object obj = type.newInstance();
                    Field[] fields = type.getDeclaredFields();
                    for (Field field : fields) {
                        boolean hasProp = false;
                        for (CommandParam gParam : gParams) {
                            if (gParam.getProp().equals(field.getName())) {
                                field.setAccessible(true);
                                try {
                                    field.set(obj, ConvertUtil.convert(gParam.getValue(), field.getType()));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                hasProp = true;
                            }
                        }
                        if (!hasProp) {
                            Default def = field.getAnnotation(Default.class);
                            if (def != null) {
                                try {
                                    field.set(obj, ConvertUtil.convert(def.value(), type));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                    invokeArgsMap.put(name, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                    invokeArgsMap.put(name, null);
                }
            }
        }
        return invokeArgsMap;
    }

    /***
     * 获取正则和@Command注解中props对应的键值
     * @param regex
     * @param command
     * @param props
     * @return
     */
    private List<CommandParam> getCommandParams(String regex, String command, List<String> props) {
        Matcher matcher = RegexUtil.getMatcher(regex, command);
        List<CommandParam> params = new ArrayList<>();
        for (int i = 0; i < props.size(); i++) {
            String prop = props.get(i);
            CommandParam param = new CommandParam();
            param.setProp(prop);
            param.setValue(matcher.groupCount() > i ? matcher.group(i + 1) : null);
            params.add(param);
        }
        return params.stream().filter(param -> !param.getProp().equalsIgnoreCase("null")).collect(Collectors.toList());
    }
}
